module klang.编译.命令行.命令行_;
import 全局;
import std.conv: ·to = to;
import std.functional: ·fd = toDelegate;
import ··枚举值_ = klang.编译.类型.枚举值.枚举值_;
import ··类型_ = klang.编译.类型.类型_;
import ··节点_ = klang.编译.语法节点.节点_;
import 核心 = klang.编译.核心.核心_;
import 系统 = klang.编译.系统.系统_;
import 工具 = klang.编译.工具.工具_;
import 编译数字 = 编译数字.编译;
import 注册 = klang.编译.注册.注册_;
import 路径 = klang.编译.路径.路径_;
import klang.编译.编译.编译_: 创建源文件;
import klang.编译.类型.枚举值.枚举值_: 字符_, SK;
import klang.编译.工具.工具_: 创建文件诊断消息;
import klang.编译.诊断集.诊断集_: 诊断集_;
import klang.编译.诊断.诊断_: 诊断消息_, 诊断_, 诊断包括位置_;
import klang.编译.类型.类型_: 命令行选项_, 前端接口_;
import klang.编译.json.JSON: JSON类型_, JSON值_, 创建JSON空对象;
import klang.编译.语法节点.节点_: 节点数组_;
import klang.json.语法节点.json节点_: JSK, Json节点_, Json源文件_, 表达式语句_, 对象字面量_, 属性赋值表达式_, 表达式_, 一元前缀表达式_, 文本字面量_, 数字字面量_, 数组字面量_;
klang.编译.命令行.命令行_.OptionNameMap optionNameMapCache;
struct OptionNameMap {
    核心.词典_!(命令行选项_) optionNameMap;
    核心.词典_!(dstring) shortOptionNames;
    this(核心.词典_!(命令行选项_) op, 核心.词典_!(dstring) sop) {
        this.optionNameMap = op;
        this.shortOptionNames = sop;
    }
}
struct 编译的命令行_ {
    JSON值_ 选项;
    dstring[] 文件组;
    诊断_[] 错误组;
    dstring 语言id;
    this(JSON值_ 选项, dstring[] 文件组, 诊断_[] 错误组, dstring 语言id) {
        this.选项 = 选项;
        this.文件组 = 文件组;
        this.错误组 = 错误组;
        this.语言id = 语言id;
    }
}
/*@internal*/
interface JsonConversionNotifier {
    void onSetValidOptionKeyValueInParent(dstring parentOption, 命令行选项_ option, JSON值_ value);
}
OptionNameMap getOptionNameMap(dstring 语言id) {
    if (((optionNameMapCache.optionNameMap is null) || (optionNameMapCache.optionNameMap.原型 is ··null!(命令行选项_[dstring]))) && ((optionNameMapCache.shortOptionNames is null) || (optionNameMapCache.shortOptionNames.原型 is ··null!(dstring[dstring])))) {
        前端接口_ 前端 = 注册.获取注册的前端(注册.获取注册的前端id(语言id));
        if (前端 !is ··null!(前端接口_)) {
            .optionNameMapCache = .createOptionNameMap(前端.获取编译选项组());
        }
        else {
            throw new Throwable(·to!(string)((`前端未注册`d ~ 语言id ~ `语言`d)));
        }
    }
    return .optionNameMapCache;
}
dstring 查找配置文件(dstring 搜索路径, bool delegate(dstring) 文件存在, dstring 配置文件名) {
    return 路径.循环每个祖先目录!(dstring)(搜索路径, (dstring 父目录) {
        dstring 文件名称 = 路径.组合路径(父目录, [配置文件名]);
        if (文件存在(文件名称)) {
            return 文件名称;
        }
        return ··null!(dstring);
    });
}
/*@internal*/
OptionNameMap createOptionNameMap(命令行选项_[] optionDeclarations) {
    核心.词典_!(命令行选项_) optionNameMap = 核心.创建词典!(命令行选项_)();
    核心.词典_!(dstring) shortOptionNames = 核心.创建词典!(dstring)();
    核心.循环每个!(命令行选项_, void*)(optionDeclarations, (命令行选项_ v, size_t i) {
        ((命令行选项_ option, size_t _) {
            optionNameMap.p·设置(核心.文本转小写(option.name), option);
            if (option.shortName !is ··null!(dstring)) {
                shortOptionNames.p·设置(option.shortName, option.name);
            }
        })(v, i);
        return null;
    });
    return .OptionNameMap(optionNameMap, shortOptionNames);
}
命令行选项_ getOptionDeclarationFromName(OptionNameMap delegate() getOptionNameMap, dstring optionName, bool allowShort = false) {
    optionName = 核心.文本转小写(optionName);
    OptionNameMap nameMap = getOptionNameMap();
    核心.词典_!(命令行选项_) optionNameMap = nameMap.optionNameMap;
    核心.词典_!(dstring) shortOptionNames = nameMap.shortOptionNames;
    // Try to translate short option names to their full equivalents.
    if (allowShort) {
        if (shortOptionNames.具有(optionName)) {
            optionName = shortOptionNames.获取(optionName);
        }
    }
    if (optionNameMap.具有(optionName)) {
        return optionNameMap.获取(optionName);
    }
    return ··null!(命令行选项_);
}
编译的命令行_ 命令行编译(dstring[] 命令行, dstring delegate(dstring) 读文件) {
    return .命令行编译平台(cast(OptionNameMap delegate(dstring))(·fd(&.getOptionNameMap)), 诊断集_.Unknown_compiler_option_0, 诊断集_.Compiler_option_0_expects_an_argument, 命令行, cast(dstring delegate(dstring))(读文件));
}
/** parseCommandLineWorker */
编译的命令行_ 命令行编译平台(OptionNameMap delegate(dstring) getOptionNameMap, 诊断消息_ unknownOptionDiagnostic, 诊断消息_ optionTypeMismatchDiagnostic, dstring[] commandLine, dstring delegate(dstring) readFile) {
    void delegate(dstring[]) parseStrings;
    void delegate(dstring) parseResponseFile;
    诊断_[] errors;
    JSON值_ options;
    dstring 语言id;
    dstring[] 命令行文件;
    void parseStrings·func(dstring[] args) {
        int i;
        i = 0;
        while(i < args.length) {
            dstring s = args[i];
            ++i;
            if (s[0] == 字符_.艾特) {
                parseResponseFile(s[1 .. $]);
            }
            else if (s[0] == 字符_.减号) {
                int index = s[1] == 字符_.减号 ? 2 : 1;
                命令行选项_ opt = .getOptionDeclarationFromName(() { return getOptionNameMap(语言id); }, s[index .. $], /*allowShort*/ true);
                if (opt !is ··null!(命令行选项_)) {
                    if (opt.只可在配置文件) {
                        errors = .追加!(诊断_)(errors, 工具.创建编译诊断(诊断集_.Option_0_can_only_be_specified_in_kconfig_json_file, [opt.name]));
                    }
                    else {
                        // Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
                        if ((i == args.length || (args[i] is ··null!(dstring))) && opt.type != "boolean"d) {
                            errors = .追加!(诊断_)(errors, 工具.创建编译诊断(optionTypeMismatchDiagnostic, [opt.name]));
                        }
                        switch (opt.type) {
                            case "number"d: {
                                options.f·设置!(int)(opt.name, 编译数字.编译!(int)(args[i], 10));
                                ++i;
                                break;
                            }
                            case "boolean"d: {
                                // boolean flag has optional value true, false, others
                                dstring optValue = i == args.length ? "true"d : args[i];
                                options.设置(opt.name, optValue != "false"d ? true : false);
                                // consume next argument as boolean flag value
                                if (optValue == "false"d || optValue == "true"d) {
                                    ++i;
                                }
                                break;
                            }
                            case "string"d: {
                                options.f·设置!(dstring)(opt.name, args[i]);
                                ++i;
                                break;
                            }
                            case "list"d: {
                                options.f·设置!(JSON值_[])(opt.name, .parseListTypeOption(opt, args[i], errors));
                                ++i;
                                break;
                            }
                            default: {
                                dstring 自定义 = .parseCustomTypeOption(opt, args[i], errors);
                                if (自定义 !is ··null!(dstring)) {
                                    options.f·设置!(dstring)(opt.name, 自定义);
                                }
                                ++i;
                                break;
                            }
                        }
                    }
                }
                else {
                    errors = .追加!(诊断_)(errors, 工具.创建编译诊断(unknownOptionDiagnostic, [s]));
                }
            }
            else {
                命令行文件 = .追加!(dstring)(命令行文件, s);
            }
        }
    }
    parseStrings = &parseStrings·func;
    void parseResponseFile·func(dstring fileName) {
        dstring text;
        dstring[] args;
        int pos;
        text = readFile(fileName);
        if (text is ··null!(dstring)) {
            errors = .追加!(诊断_)(errors, 工具.创建编译诊断(诊断集_.没发现文件_0, [fileName]));
            return;
        }
        args = [];
        pos = 0;
        while(true) {
            while(pos < text.length && text[pos] <= 字符_.空格) {
                ++pos;
            }
            if (pos >= text.length) {
                break;
            }
            int start = pos;
            if (text[start] == 字符_.双引) {
                ++pos;
                while(pos < text.length && text[pos] != 字符_.双引) {
                    ++pos;
                }
                if (pos < text.length) {
                    args = .追加!(dstring)(args, text[start + 1 .. pos]);
                    ++pos;
                }
                else {
                    errors = .追加!(诊断_)(errors, 工具.创建编译诊断(诊断集_.Unterminated_quoted_string_in_response_file_0, [fileName]));
                }
            }
            else {
                while(text[pos] > 字符_.空格) {
                    ++pos;
                }
                args = .追加!(dstring)(args, text[start .. pos]);
            }
        }
        parseStrings(args);
    }
    parseResponseFile = &parseResponseFile·func;
    errors = [];
    options = 创建JSON空对象();
    语言id = ""d;
    if (commandLine.length >= 1) {
        语言id = commandLine[0];
        commandLine = commandLine[1 .. $];
    }
    parseStrings(commandLine);
    命令行文件 = [];
    return .编译的命令行_(options, 命令行文件, errors, 语言id);
}
/* @internal */
诊断_ createCompilerDiagnosticForInvalidCustomType(命令行选项_ opt) {
    return .createDiagnosticForInvalidCustomType(opt, cast(诊断_ delegate(诊断消息_, dstring[]))(·fd(&工具.创建编译诊断)));
}
诊断_ createDiagnosticForInvalidCustomType(命令行选项_ opt, 诊断_ delegate(诊断消息_, dstring[]) createDiagnostic) {
    dstring[] namesOfType = [];
    foreach (dstring k, dstring _; opt.types) {
        namesOfType = .追加!(dstring)(namesOfType, k);
    }
    dstring names = 核心.连接(namesOfType, ", "d);
    return createDiagnostic(诊断集_.Argument_for_0_option_must_be_Colon_1, [`--`d ~ opt.name ~ ``d, names]);
}
dstring convertJsonOptionOfCustomType(命令行选项_ opt, dstring value, 诊断_[] errors) {
    if (value == ""d) {
        return ··null!(dstring);
    }
    if (opt.type == "list"d) {
    }
    else if (opt.types !is ··null!(dstring[dstring])) {
        dstring key = 核心.文本转小写(value);
        dstring[dstring] map = opt.types;
        if (.具有!(dstring, dstring)(map, key) !is ··null!(dstring*)) {
            return map[key];
        }
        else {
            errors = .追加!(诊断_)(errors, .createCompilerDiagnosticForInvalidCustomType(opt));
        }
    }
    return value;
}
/* @internal */
dstring parseCustomTypeOption(命令行选项_ opt, dstring value, 诊断_[] errors) {
    return .convertJsonOptionOfCustomType(opt, 核心.裁剪文本(value), errors);
}
JSON值_[] parseListTypeOption(命令行选项_ opt, dstring value, 诊断_[] errors) {
    value = 核心.裁剪文本(value);
    if (核心.开始包含(value, "-"d) || value == ""d) {
        return [];
    }
    dstring[] values = 核心.分割(value, [","d]);
    switch (opt.element.type) {
        case "string"d:
        case "number"d: {
            return 核心.映射!(dstring, JSON值_)(values, (dstring v, size_t _) { return JSON值_(v); });
        }
        default: {
            return 核心.映射定义的!(dstring, JSON值_)(values, (dstring v, size_t _) { return JSON值_(.parseCustomTypeOption(*opt.element, v, errors)); });
        }
    }
}
/*@internal*/
JSON值_ convertToObjectWorker(Json源文件_ 源文件, 诊断_[] errors, bool returnValue, 命令行选项_ knownRootOptions) {
    bool delegate(命令行选项_[dstring]) isRootOptionMap;
    诊断包括位置_ delegate(Json源文件_, Json节点_, 诊断消息_, dstring[]) createDiagnosticForNodeInSourceFile;
    JSON值_ delegate(对象字面量_, 命令行选项_[dstring], 诊断消息_, dstring) convertObjectLiteralExpressionToJson;
    JSON值_[] delegate(节点数组_!(Json节点_), 命令行选项_) convertArrayLiteralExpressionToJson;
    JSON值_ delegate(Json节点_, 命令行选项_) convertPropertyValueToJson;
    ··节点_.节点_ 表达式语句;
    Json节点_ 表达式;
    bool isRootOptionMap·func(命令行选项_[dstring] knownOptions) {
        return knownRootOptions.elementOptions == knownOptions;
    }
    isRootOptionMap = &isRootOptionMap·func;
    诊断包括位置_ createDiagnosticForNodeInSourceFile·func(Json源文件_ sou, Json节点_ node, 诊断消息_ 诊断消息, dstring[] arr_0) {
        arr_0 = arr_0.dup;
        return 创建文件诊断消息(sou, node.开始, node.结束 - node.开始, 诊断消息, arr_0);
    }
    createDiagnosticForNodeInSourceFile = &createDiagnosticForNodeInSourceFile·func;
    JSON值_ convertObjectLiteralExpressionToJson·func(对象字面量_ node, 命令行选项_[dstring] knownOptions, 诊断消息_ extraKeyDiagnosticMessage, dstring parentOption) {
        JSON值_ result;
        result = 创建JSON空对象();
        foreach (Json节点_ 元素; node.元素组) {
            if (元素.目标种类 != JSK.属性赋值表达式) {
                errors = .追加!(诊断_)(errors, createDiagnosticForNodeInSourceFile(源文件, 元素, 诊断集_.Property_assignment_expected, [""d]));
                continue;
            }
            属性赋值表达式_ element = (cast(属性赋值表达式_)(元素));
            dstring keyText = element.名称;
            命令行选项_ option = ··null!(命令行选项_);
            if (keyText != ""d && (knownOptions !is ··null!(命令行选项_[dstring])) && (.具有!(命令行选项_, dstring)(knownOptions, keyText) !is ··null!(命令行选项_*))) {
                option = knownOptions[keyText];
            }
            if (keyText != ""d && (extraKeyDiagnosticMessage !is ··null!(诊断消息_)) && (option is ··null!(命令行选项_))) {
                errors = .追加!(诊断_)(errors, createDiagnosticForNodeInSourceFile(源文件, element, extraKeyDiagnosticMessage, [keyText]));
            }
            JSON值_ value = convertPropertyValueToJson(element.表达式, option);
            if (keyText != ""d) {
                if (returnValue) {
                    result.设置(keyText, value);
                }
            }
        }
        return result;
    }
    convertObjectLiteralExpressionToJson = &convertObjectLiteralExpressionToJson·func;
    JSON值_[] convertArrayLiteralExpressionToJson·func(节点数组_!(Json节点_) elements, 命令行选项_ elementOption) {
        JSON值_[] 组;
        JSON值_[] 过滤后的;
        if (!returnValue) {
            return 核心.映射!(Json节点_, JSON值_)(elements, (表达式_ 元素, size_t _) { return convertPropertyValueToJson(元素, elementOption); });
        }
        组 = 核心.映射!(Json节点_, JSON值_)(elements, (表达式_ 元素, size_t _) { return convertPropertyValueToJson(元素, elementOption); });
        过滤后的 = 核心.过滤!(JSON值_)(组, (JSON值_ v) { return v.类型 != JSON类型_.空值_; });
        if (过滤后的 !is ··null!(JSON值_[])) {
            return 过滤后的;
        }
        return [];
    }
    convertArrayLiteralExpressionToJson = &convertArrayLiteralExpressionToJson·func;
    JSON值_ convertPropertyValueToJson·func(Json节点_ valueExpression, 命令行选项_ option) {
        void delegate(bool) reportInvalidOptionValue;
        void reportInvalidOptionValue·func(bool isError) {
            if (isError) {
                errors = .追加!(诊断_)(errors, createDiagnosticForNodeInSourceFile(源文件, valueExpression, 诊断集_.Compiler_option_0_requires_a_value_of_type_1, [option.name, .getCompilerOptionValueTypeString(option)]));
            }
        }
        reportInvalidOptionValue = &reportInvalidOptionValue·func;
        switch (valueExpression.目标种类) {
            case JSK.真_: {
                reportInvalidOptionValue(option.type != "boolean"d);
                return JSON值_(true);
            }
            case JSK.假_: {
                reportInvalidOptionValue(option.type != "boolean"d);
                return JSON值_(false);
            }
            case JSK.空值_: {
                reportInvalidOptionValue(option.name == "extends"d); // "extends" is the only option we don't allow null/零值 循环
                return JSON值_();
            }
            case JSK.文本字面量: {
                reportInvalidOptionValue(option.type != "string"d && option.type != "Map"d);
                文本字面量_ 文本节点 = (cast(文本字面量_)(valueExpression));
                dstring text = 文本节点.内容;
                if (option.types !is ··null!(dstring[dstring])) {
                    reportInvalidOptionValue(option.type != "Map"d);
                    命令行选项_ customOption = option;
                    // Validate custom option type
                    if ((customOption.types !is ··null!(dstring[dstring])) && (.具有!(dstring, dstring)(customOption.types, text) is ··null!(dstring*))) {
                        errors = .追加!(诊断_)(errors, .createDiagnosticForInvalidCustomType(customOption, (诊断消息_ message, dstring[] arg0) { return createDiagnosticForNodeInSourceFile(源文件, valueExpression, message, arg0); }));
                    }
                }
                return JSON值_(text);
            }
            case JSK.数字字面量: {
                reportInvalidOptionValue(option.type != "number"d);
                数字字面量_ n = (cast(数字字面量_)(valueExpression));
                return JSON值_(n.值);
            }
            case JSK.一元前缀表达式: {
                一元前缀表达式_ 前缀表达式 = (cast(一元前缀表达式_)(valueExpression));
                if (前缀表达式.运算符 != SK.减号 || 前缀表达式.表达式.目标种类 != JSK.数字字面量) {
                    break; // not valid JSON syntax
                }
                reportInvalidOptionValue(option.type != "number"d);
                数字字面量_ 数字表达式 = (cast(数字字面量_)(前缀表达式.表达式));
                return JSON值_(-数字表达式.值);
            }
            case JSK.对象字面量: {
                reportInvalidOptionValue(option.type != "object"d);
                对象字面量_ objectLiteralExpression = (cast(对象字面量_)(valueExpression));
                // Currently having element option declaration in the tsconfig with type "object"
                // determines 如果 it needs onSetValidOptionKeyValueInParent callback or not
                // At moment there are only "compilerOptions", "typeAcquisition" and "typingOptions"
                // that satifies it and need it to modify options set in them (循环 normalizing file paths)
                // vs what we set in the json
                // If need arises, we can modify this interface and callbacks as needed
                if (option !is ··null!(命令行选项_)) {
                    dstring optionName = option.name;
                    命令行选项_[dstring] elementOptions = option.elementOptions;
                    诊断消息_ extraKeyDiagnosticMessage = option.extraKey诊断消息_;
                    return convertObjectLiteralExpressionToJson(objectLiteralExpression, elementOptions, extraKeyDiagnosticMessage, optionName);
                }
                else {
                    return convertObjectLiteralExpressionToJson(objectLiteralExpression, /* knownOptions*/ ··null!(命令行选项_[dstring]), 
                    /*extraKeyDiagnosticMessage */ ··null!(诊断消息_), /*parentOption*/ ··null!(dstring));
                }
            }
            case JSK.数组字面量: {
                reportInvalidOptionValue(option.type != "list"d);
                数组字面量_ 数组 = (cast(数组字面量_)(valueExpression));
                return JSON值_(convertArrayLiteralExpressionToJson(数组.元素组, *(option.element)));
            }
            default: break;
        }
        // Not in expected format
        if (option !is ··null!(命令行选项_)) {
            reportInvalidOptionValue(/*isError*/ true);
        }
        else {
            errors = .追加!(诊断_)(errors, createDiagnosticForNodeInSourceFile(源文件, valueExpression, 诊断集_.Property_value_can_only_be_string_literal_numeric_literal_true_false_null_object_literal_or_array_literal, [""d]));
        }
        return ··null!(JSON值_);
    }
    convertPropertyValueToJson = &convertPropertyValueToJson·func;
    if (源文件.语句组.length == 0) {
        return 创建JSON空对象();
    }
    表达式语句 = 源文件.语句组.原型[0];
    表达式 = (cast(表达式语句_)(表达式语句)).表达式;
    return convertPropertyValueToJson(表达式, knownRootOptions);
}
auto getCompilerOptionValueTypeString(命令行选项_ option) {
    return option.type == "list"d ? "Array"d : option.type;
}
bool isCompilerOptionsValue(命令行选项_ option, JSON值_ value) {
    if (option !is ··null!(命令行选项_)) {
        if (value.类型 == JSON类型_.空值_) {
            return true;
        }
        if (option.type == "list"d) {
            return value.类型 == JSON类型_.数组_;
        }
        if (option.type == "string"d) {
            return value.类型 == JSON类型_.文本_;
        }
        if (option.type == "number"d) {
            return value.类型 == JSON类型_.整数_;
        }
        if (option.type == "boolean"d) {
            return value.类型 == JSON类型_.真_ || value.类型 == JSON类型_.假_;
        }
        if (option.type == "object"d) {
            return value.类型 == JSON类型_.对象_;
        }
    }
    return false;
}
编译的命令行_ 编译配置文件(dstring 配置文件名, dstring delegate(dstring) 读文件, 命令行选项_ 根选项) {
    前端接口_ json前端 = 注册.获取注册前端且执行初始化("json"d);
    dstring 内容 = 读文件(配置文件名);
    ··节点_.源文件_ sou = 创建源文件(配置文件名, 内容, json前端);
    dstring[] 命令行文件 = [];
    if (sou.编译诊断 !is ··null!(诊断_[])) {
        JSON值_ 空JSON = 创建JSON空对象();
        return .编译的命令行_(空JSON, 命令行文件, .转换诊断(sou.编译诊断), 注册.当前前端.获取语言id());
    }
    Json源文件_ jsonSou = (cast(Json源文件_)(sou));
    assert(jsonSou !is ··null!(Json源文件_), "Json源文件_ 转换编译失败"c);
    诊断_[] errors = [];
    JSON值_ options = .编译配置文件平台(jsonSou, errors, 根选项);
    return .编译的命令行_(options, 命令行文件, errors, 注册.当前前端.获取语言id());
}
JSON值_ 编译配置文件平台(Json源文件_ 源文件, 诊断_[] errors, 命令行选项_ 根选项) {
    return .convertToObjectWorker(源文件, errors, true, 根选项);
}
诊断_[] 转换诊断(诊断_[] 诊断) {
    诊断_[] ret = [];
    foreach (诊断_ v; 诊断) {
        .追加!(诊断_)(ret, v);
    }
    return ret;
}
